1. Technical Field
The present invention is directed to the creation and use of a secondary protocol function table in connection with object construction through a class library to control object invocation and destruction in object-oriented technology, and in particular to correctly implement concurrent processing actions in an object-oriented computing environment.
2. Description of the Related Art
Object oriented technology is a relatively recent development, and there are a number of language approaches that utilize the object-oriented concepts described below, including totally new languages developed to embrace the object-oriented approach such as Simula.sup.1 and SmallTalk.sup.2, and extensions to pre-existing traditional languages, such as C++.sup.3. FNT .sup.1 Simula was the first object-oriented language developed in the late 1960's at the Norwegian Computer Center. FNT .sup.2 SmallTalk was developed in the early 1970's at the Xerox PARC (Palo Alto Research Center). FNT .sup.3 C++ was developed in the early 1980's at AT&T's Bell Laboratories as an object-oriented extension to the non-object-oriented C programming language.
C++ can be processed as a directly compiled language or implemented as a preprocessor to C, a rapid processing language that is currently very popular. Accordingly, C++ is very portable to any system with either a C++ or C compiler. As a result, C++ has become one of the most widely used object-oriented models today.
In object-oriented technology, computer simulated "objects", modelled on real world objects, are defined and maintained as self-contained units, independent of each other. The definition of an object includes its "methods" or member functions (what procedures or operations the object is capable of performing) and its "variables" (its attributes, the values of which can change over time). However, the manner in which these internal values are implemented in an object is not visible outside the object.
The methods and attributes defined for a particular object are determined by a "class", a template that defines the methods and variables to be included in a particular type of object. Moreover, the approach to object creation is hierarchical, and classes are defined in terms of higher classes in the hierarchy, that is, the characteristics (methods and/or variables) of a subordinate or subclass are "inherited" from a superior, more generalized base or superclass, and those inherited characteristics form the parameters for the object's methods or variables.
In many computer systems, storage mechanisms known as "class libraries" serve as repositories for the class hierarchy and data, and when an object is called through a processor, the appropriate data is channeled from the class library to hardware-recognized areas of dynamic memory storage known as the heap and the stack.
Because of the invisibility of an object's internal structure, it is not possible to determine, externally, the particular inheritance path of any of the object's methods or attributes, and when one object calls for method implementation of another object, it must do so through method invocation, that is, calling on the other object to perform its method.
In object-oriented technology, objects actually interact by sending and receiving messages. A message is a signal from one object to another that requests the receiving object to carry out one of its methods. The message consists of the name of the receiving object and its arguments, that is, the method it is to carry out and any parameters the method may require to fulfill its charge.
Method invocation is fundamental to object-oriented technology, and is attractive from the point of view of optimizing compile time, since an operating program need only contain a single invocation call, and this call will implement the mechanism to perform the method called outside of the operating program.
However, one limitation on the use of method invocation in compiled languages such as C++ arises from the fact that C++ is an inherently sequential language without any provision for accommodating non-sequential method invocation, such as the concurrent processing of independent applications or threads of control in a single application.
Problems that can arise through non-sequential method invocation in C++ include premature method invocation before the object has been completely constructed, and premature object destruction before the object's method has been completely executed.
Approaches to providing concurrency in object-oriented languages such as C++ have been through the addition of enhancements to C compilers, including compilers for C language extensions. In U.S. Pat. No. 5,179,702--Spix et al., an "optimizer" placed between the front and back ends of the compiler functions to analyze a program and optimize its processing through several means, including "multi-threading" (concurrently applying multiple processors to a single program). The optimizer generates machine independent code to establish the environment, including the dynamic allocation of a set of global registers for use by the shared processing group.
A second approach to addressing this problem has been to extend the C++ language to introduce the ability to create concurrent structures in memory in order to accommodate synchronous thread processing. For example, the .mu.C++ language extension.sup.4 consists of a set of programming language constructs needed to express concurrency. FNT .sup.4 P. A. Buhr, et al., ".mu.C++: Concurrency in the Object-Oriented Language C++", Software Practice and Experience, 22(2), February 1992.
Another approach to providing concurrency believed to have better portability than either language extensions or enhanced compilers is through the incorporation of a mechanism permitting the control of method invocation in a class library associated with the C++ programming language. C++ permits the inclusion of abstract or virtual classes, which are classes with no instances created only for the purpose of organizing a class hierarchy or defining methods and variables that will apply to lower level classes. The abstract or virtual class created (often called a "task class") creates the constructors for concurrent processing.
There are several examples of this in the prior art, one of which is PRESTO.sup.5. PRESTO is in fact an entire class library for the creation and management of threads, with separate classes for each concurrency. FNT .sup.5 B. N. Bershad, et al., "PRESTO: A System for Object-Oriented Parallel Programming", Software Practice and Experience, 18(8), August 1988.
Rather than providing a complete class library for object concurrency, it has also been proposed to add a discrete "tasking package" to an existing class library (Doeppner, et al.).sup.6. In the Doeppner model, the tasking package consists of four C++ classes to be used by the programmer as base classes to build their own classes for parallel computations. The first of these is the task class, and the constructors of any class derived from this task class run as a separate thread control in the program. The package also includes a second class, called monitor, which defines a set of routines to make a derived thread act as a monitor for the controlled sharing of the data between different tasks. Two additional classes, QHEAD and QTAIL, define a one way queue for communication between tasks. FNT .sup.6 T. W. Doeppner Jr. and A. J. Gable, "C++ on a Parallel Machine", CS-87-26, Brown University Department of Computer Science, November 1987.
Other proposals for specific abstract "task like" classes are found for directed purposes, as in U.S. Pat. No. 5,142,674--Barker et al, relating to the use of an abstract class in an object-oriented document or local library for constructing application dependent indices to an object-oriented document database for random object retrieval.
In class libraries, such as Doeppner, which implement an "active object" abstraction, it is the job of the task constructor to spin off an independent thread of control for instances of any class for which the task class is a base class.
Every time such an object is created, a series of constructors are invoked, one of them being the constructor for the task class. In normal executions of constructor invocation in C++, once space for a newly allocated object is reserved on the heap, its constructor is called. This will be the constructor defined (if one is defined) for the deepest level of inheritance. Because C++ specifies that these class constructors be executed before derived class constructors are executed, this deepest level of inheritance will immediately call the constructor for its base class (at the second deepest level of inheritance) before execution. The called base class will in turn immediately call its own base constructor, and so on. Finally, the constructor for the task class will be called (assuming that the task class has been used as the base class). Once the task constructor finishes and returns, the constructor that called it will perform its construction duties (which may involve calling constructors from member objects), and will then return to the constructor that called it. This continues until the first constructor is allowed to do its construction. This process is referred to as the invocation chain.
When the constructor for the task class is called, it generates a new thread of control, then directs the original thread to jump to the point in the program (called the originating call point) which immediately follows the invocation of the constructors, while the other thread continues executing the constructors. Typically in such libraries, the program makes the constructor for the derived class an infinite or semi-infinite loop, thus allowing the constructor thread to become an independent "activity" for the object.
In none of these prior applications for affecting concurrent processing is any provision made for message passing in the guise of C++ method invocation. Generally, communication happens solely with the use of shared variables and explicit message queues.
The reason for this is that C++ provides no native mechanism for selectively controlling which methods (or member functions) of an object are invocable.